// **********************************************************************
// 
// <copyright>
// 
//  BBN Technologies
//  10 Moulton Street
//  Cambridge, MA 02138
//  (617) 873-8000
// 
//  Copyright (C) BBNT Solutions LLC. All rights reserved.
// 
// </copyright>
// **********************************************************************
// 
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/rpf/RpfLayer.java,v $
// $RCSfile: RpfLayer.java,v $
// $Revision: 1.23 $
// $Date: 2008/09/17 20:47:51 $
// $Author: dietrick $
// 
// **********************************************************************

package com.bbn.openmap.layer.rpf;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
/*  Java Core  */
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Serializable;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JCheckBox;
import javax.swing.JPanel;

import com.bbn.openmap.I18n;
import com.bbn.openmap.event.SelectMouseMode;
import com.bbn.openmap.layer.OMGraphicHandlerLayer;
import com.bbn.openmap.layer.policy.ListResetPCPolicy;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.omGraphics.OMList;
import com.bbn.openmap.omGraphics.OMRaster;
import com.bbn.openmap.omGraphics.event.MapMouseInterpreter;
import com.bbn.openmap.proj.EqualArc;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.PropUtils;
import com.bbn.openmap.util.cacheHandler.CacheHandler;

/**
 * The RpfLayer fills the screen with RPF data. There is also a tool available
 * that allows you to see the coverage of the available data. To view theimages,
 * the projection of the map has to be set in the ARC projection, which OpenMap
 * calls the CADRG projection. The RpfLayer can use several RPF directories at
 * the same time, and doesn't require that the data actually be there at
 * runtime. That way, you can give a location where the data may be mouted
 * during runtime(i.e. CDROM) and the layer will still use the data. The scale
 * of the projection does not necessarily have to match the scale of a map
 * series for that series to be displayed. There are options, set in the
 * RpfViewAttributes, that allow scaling of the RPF images to match the map
 * scale.
 * <P>
 * 
 * The RpfLayer uses the RpfCacheManager to get the images it needs to display.
 * Whenever the projection changes, the cache manager takes the new projection
 * and creates a OMGraphicList with the new image frames and attribute text.
 * <P>
 * 
 * The RpfLayer gets its initial settings from properties. This should be done
 * right after the RpfLayer is created. The properties list contains the
 * location of the RPF directories, the opaqueness of the images, the number of
 * colors to use, and whether to show the images and/or attributes by default.
 * An example of the RpfLayer properties:
 * <P>
 * 
 * <pre>
 * 
 * 
 *           #-----------------------------
 *           # Properties for RpfLayer
 *           #-----------------------------
 *           # Mandatory properties
 *           # This property should reflect the paths to the RPF directories
 *           rpf.paths=/usr/local/matt/data/RPF;/usr/local/matt/data/CIB/RPF
 *           
 *           # Optional Properties - the default will be set if these are not 
 *           # included in the properties file: 
 *           # Number between 0-255: 0 is transparent, 255 is opaque.  255 is default.
 *           rpf.opaque=128
 *           
 *           # Number of colors to use on the maps - 16, 32, 216.  216 is default.
 *           rpf.numberColors=216
 *           
 *           # Display maps on startup.  Default is true.
 *           rpf.showMaps=true
 *           
 *           # Display attribute information on startup.  Default is false.
 *           rpf.showInfo=false
 *           
 *           # Scale charts to match display scale.  Default is true.
 *           rpf.scaleImages=true
 *           
 *           # The scale factor to allow when scaling images (2x, 4x, also mean 1/2, 1/4).  Default is 4.
 *           rpf.imageScaleFactor=4
 *           
 *           # Delete the cache if the layer is removed from the map.  Default is false.
 *           rpf.killCache=true
 *           # Limit the display to the chart code specified. (GN, JN, ON, TP, etc.).
 *           # Default is ANY
 *           rpf.chartSeries=ANY
 *           # Get the subframe attribute data from the Frame provider.
 *           rpf.autofetchAttributes=false
 *           # Set to true if you want the coverage tool available.
 *           rpf.coverage=true
 *           # Set the subframe cache size. (Number of subframes to hold on to, 256x256 pixels)
 *           rpf.subframeCacheSize=128
 *           # Then also include coverage properties, which are available in the RpfConstants.
 *           #------------------------------------
 *           # End of properties for RpfLayer
 *           #------------------------------------
 * 
 * 
 * </pre>
 * 
 */
public class RpfLayer extends OMGraphicHandlerLayer implements ActionListener, RpfConstants, Serializable {

	protected static Logger logger = Logger.getLogger("com.bbn.openmap.layer.rpf.RpfLayer");
	protected static Logger rpfLogger = Logger.getLogger("RPF");

	private static final long serialVersionUID = 1L;
	/**
	 * The main source for the images and attribute information. All requests
	 * for graphic objects should go through this cache, and it will
	 * automatically handle getting the frame files, decoding them, and
	 * returning an object list.
	 */
	protected transient RpfCacheManager cache = null;
	/** The paths to the RPF directories, telling where the data is. */
	protected String[] paths;
	/**
	 * The display attributes for the maps. This object should not be replaced,
	 * because the caches all look at it, too. Just adjust the parameters within
	 * it.
	 * 
	 * @see RpfViewAttributes
	 */
	protected RpfViewAttributes viewAttributes;
	/** Flag to delete the cache if the layer is removed from the map. */
	protected boolean killCache = true;
	/** The supplier of frame data. */
	protected RpfFrameProvider frameProvider;
	/** The coverage tool for the layer. */
	protected RpfCoverage coverage;
	/** Subframe cache size. Default is 40. */
	protected int subframeCacheSize = RpfCacheHandler.SUBFRAME_CACHE_SIZE;
	/** Auxiliary subframe cache size. Default is 10. */
	protected int auxSubframeCacheSize = RpfCacheManager.SMALL_CACHE_SIZE;

	/**
	 * The default constructor for the Layer. All of the attributes are set to
	 * their default values. Use this construct if you are going to use a
	 * standard properties file, which will set the paths.
	 */
	public RpfLayer() {
		setName("RPF");
		viewAttributes = new RpfViewAttributes();
		setProjectionChangePolicy(new ListResetPCPolicy(this));
		// setRenderPolicy(new BufferedImageRenderPolicy(this));
		setMouseModeIDsForEvents(new String[] { SelectMouseMode.modeID });

		showSubframes(false);
	}

	/**
	 * The default constructor for the Layer. All of the attributes are set to
	 * their default values.
	 * 
	 * @param pathsToRPFDirs
	 *            paths to the RPF directories that hold A.TOC files.
	 */
	public RpfLayer(String[] pathsToRPFDirs) {
		this();
		setPaths(pathsToRPFDirs);
	}

	/**
	 * Set the paths to the RPF directories, which are by default the parents of
	 * the A.TOC table of contents files. Creates the RpfFrameProvider.
	 * 
	 * @param pathsToRPFDirs
	 *            Array of strings that list the paths to RPF directories.
	 */
	public void setPaths(String[] pathsToRPFDirs) {
		if (paths != null && pathsToRPFDirs != null && paths.length == pathsToRPFDirs.length) {
			// If the paths haven't changed, don't do anything.
			boolean same = true;
			for (int i = 0; i < paths.length; i++) {
				same = same && paths[i].equals(pathsToRPFDirs[i]);
			}

			if (same && frameProvider != null) {
				return;
			}
		}

		if (pathsToRPFDirs != null) {
			setFrameProvider(new RpfFrameCacheHandler(pathsToRPFDirs));
		} else {
			logger.warning("Need RPF directory paths.");
			frameProvider = null;
		}
		paths = pathsToRPFDirs;

		setCoverage(new RpfCoverage(this, frameProvider));

		this.cache = null;
	}

	/**
	 * Get the paths to the RPF directories.
	 * 
	 * @return String[]
	 */
	public String[] getPaths() {
		return paths;
	}

	/**
	 * Called when the layer is no longer part of the map. In this case, we
	 * should disconnect from the server if we have a link.
	 */
	public void removed(java.awt.Container cont) {
		if (killCache) {
			rpfLogger.fine("emptying cache!");
			clearCache();
		}

		// need to reset this for when it gets added again, if it was
		// removed without the projection actually changing. This
		// helps when the cache needs to be rebuilt.
		setProjection((Projection) null);
	}

	protected void setDefaultValues() {
		// defaults
		paths = null;
	}

	/**
	 * Set all the RPF properties from a properties object.
	 */
	public void setProperties(String prefix, java.util.Properties properties) {

		super.setProperties(prefix, properties);

		prefix = PropUtils.getScopedPropertyPrefix(prefix);

		setPaths(PropUtils.initPathsFromProperties(properties, prefix + RpfPathsProperty, paths));

		viewAttributes.setProperties(prefix, properties);
		showSubframes(viewAttributes.showInfo);

		subframeCacheSize = PropUtils.intFromProperties(properties, prefix + CacheSizeProperty, subframeCacheSize);

		auxSubframeCacheSize = PropUtils.intFromProperties(properties, prefix + CacheSizeProperty,
				auxSubframeCacheSize);

		if (viewAttributes.chartSeries == null)
			viewAttributes.chartSeries = RpfViewAttributes.ANY;

		killCache = PropUtils.booleanFromProperties(properties, prefix + KillCacheProperty, killCache);

		if (coverage != null) {
			coverage.setProperties(prefix, properties);
		}

		resetPalette();
	}

	/**
	 * PropertyConsumer method, to fill in a Properties object, reflecting the
	 * current values of the layer. If the layer has a propertyPrefix set, the
	 * property keys should have that prefix plus a separating '.' prepended to
	 * each property key it uses for configuration.
	 * 
	 * @param props
	 *            a Properties object to load the PropertyConsumer properties
	 *            into. If props equals null, then a new Properties object
	 *            should be created.
	 * @return Properties object containing PropertyConsumer property values. If
	 *         getList was not null, this should equal getList. Otherwise, it
	 *         should be the Properties object created by the PropertyConsumer.
	 */
	public Properties getProperties(Properties props) {
		props = super.getProperties(props);
		String prefix = PropUtils.getScopedPropertyPrefix(this);

		// find out paths...
		String[] p = getPaths();
		StringBuffer pathString = new StringBuffer();
		if (p != null) {
			for (int i = 0; i < p.length; i++) {
				if (p[i] != null) {
					pathString.append(p[i]);
					if (i < p.length - 1) {
						pathString.append(";"); // separate paths with
						// ;
					}
				}
			}
			props.put(prefix + RpfPathsProperty, pathString.toString());
		} else {
			props.put(prefix + RpfPathsProperty, "");
		}

		props.put(prefix + KillCacheProperty, new Boolean(killCache).toString());
		props.put(prefix + CacheSizeProperty, Integer.toString(subframeCacheSize));
		props.put(prefix + AuxCacheSizeProperty, Integer.toString(auxSubframeCacheSize));

		viewAttributes.setPropertyPrefix(prefix);
		viewAttributes.getProperties(props);

		if (coverage == null) {
			RpfCoverage cov = new RpfCoverage(this, frameProvider);
			cov.setProperties(prefix, new Properties());
			cov.getProperties(props);
		} else {
			coverage.getProperties(props);
		}

		return props;
	}

	/**
	 * Method to fill in a Properties object with values reflecting the
	 * properties able to be set on this PropertyConsumer. The key for each
	 * property should be the raw property name (without a prefix) with a value
	 * that is a String that describes what the property key represents, along
	 * with any other information about the property that would be helpful
	 * (range, default value, etc.). For Layer, this method should at least
	 * return the 'prettyName' property.
	 * 
	 * @param list
	 *            a Properties object to load the PropertyConsumer properties
	 *            into. If getList equals null, then a new Properties object
	 *            should be created.
	 * @return Properties object containing PropertyConsumer property values. If
	 *         getList was not null, this should equal getList. Otherwise, it
	 *         should be the Properties object created by the PropertyConsumer.
	 */
	public Properties getPropertyInfo(Properties list) {
		list = super.getPropertyInfo(list);
		String interString;

		interString = i18n.get(RpfLayer.class, RpfPathsProperty, I18n.TOOLTIP,
				"Paths to RPF directories.  Semi-colon separated paths.");
		list.put(RpfPathsProperty, interString);
		list.put(RpfPathsProperty + ScopedEditorProperty,
				"com.bbn.openmap.util.propertyEditor.MultiDirectoryPropertyEditor");
		interString = i18n.get(RpfLayer.class, RpfPathsProperty, "Data Path");
		list.put(RpfPathsProperty + LabelEditorProperty, interString);

		interString = i18n.get(RpfLayer.class, KillCacheProperty, I18n.TOOLTIP,
				"Flag to trigger the cache to be cleared when layer is removed from the map.");
		list.put(KillCacheProperty, interString);
		list.put(KillCacheProperty + ScopedEditorProperty, "com.bbn.openmap.util.propertyEditor.OnOffPropertyEditor");
		interString = i18n.get(RpfLayer.class, KillCacheProperty, "Clear Cache");
		list.put(KillCacheProperty + LabelEditorProperty, interString);

		interString = i18n.get(RpfLayer.class, CacheSizeProperty, I18n.TOOLTIP,
				"Number of frames to hold in the frame cache.");
		list.put(CacheSizeProperty, interString);
		interString = i18n.get(RpfLayer.class, CacheSizeProperty, "Frame Cache Size");
		list.put(CacheSizeProperty + LabelEditorProperty, interString);

		interString = i18n.get(RpfLayer.class, AuxCacheSizeProperty, I18n.TOOLTIP,
				"Number of frames to hold in aux. frame caches.");
		list.put(AuxCacheSizeProperty, interString);
		interString = i18n.get(RpfLayer.class, AuxCacheSizeProperty, "Aux Frame Cache Size");
		list.put(AuxCacheSizeProperty + LabelEditorProperty, interString);

		viewAttributes.getPropertyInfo(list);

		RpfCoverage tmpCov = coverage;
		if (tmpCov == null) {
			tmpCov = new RpfCoverage(this, frameProvider);
		}

		tmpCov.getPropertyInfo(list);

		list.put(initPropertiesProperty,
				RpfPathsProperty + " " + KillCacheProperty + " " + CacheSizeProperty + " " + AuxCacheSizeProperty + " "
						+ viewAttributes.getInitPropertiesOrder() + " " + AddToBeanContextProperty + " "
						+ AddAsBackgroundProperty + " " + RemovableProperty + " " + CoverageProperty + " "
						+ tmpCov.getInitPropertiesOrder());

		return list;
	}

	public void resetPalette() {
		box = null;
		// if (coverage != null) {
		// if (coverage.isInUse()) {
		// coverage.resetColors();
		// }
		// }

		super.resetPalette();
	}

	/**
	 * Clear the frame cache.
	 */
	public void clearCache() {

		if (frameProvider instanceof CacheHandler) {
			((CacheHandler) frameProvider).resetCache();
		}

		if (this.cache != null) {
			this.cache.setViewAttributes(null);
			this.cache.setFrameProvider(null);
			this.cache.clearCaches();
		}

		frameProvider = null;

		setList(null);
		this.cache = null;
	}

	/**
	 * Set the view attributes for the layer. The frame provider view attributes
	 * are updated, and the cache is cleared.
	 * 
	 * @param rva
	 *            the RpfViewAttributes used for the layer.
	 */
	public void setViewAttributes(RpfViewAttributes rva) {
		viewAttributes = rva;
		if (this.cache != null) {
			this.cache.setViewAttributes(rva);
		}
	}

	/**
	 * Get the view attributes or the layer.
	 * 
	 * @return RpfViewAttributes.
	 */
	public RpfViewAttributes getViewAttributes() {
		return viewAttributes;
	}

	/**
	 * Set the RpfCoverage tool used by the layer. If the view attributes chart
	 * series setting is not equal to RpfViewAttributes.ANY, then the palette of
	 * the tool is not shown.
	 * 
	 * @param cov
	 *            the RpfCoverage tool.
	 */
	public void setCoverage(RpfCoverage cov) {
		coverage = cov;
		if (coverage != null) {
			if (viewAttributes != null && !viewAttributes.chartSeries.equalsIgnoreCase(RpfViewAttributes.ANY)) {
				coverage.setShowPalette(false);
			}
			coverage.coverageManager = null;
		}
	}

	/**
	 * Return the coverage tool used by the layer.
	 * 
	 * @return RpfCoverage tool.
	 */
	public RpfCoverage getCoverage() {
		return coverage;
	}

	/**
	 * Set the RpfFrameProvider for the layer. Clears out the cache, and the
	 * frame provider gets the RpfViewAttributes held by the layer.
	 * 
	 * @param fp
	 *            the frame provider.
	 */
	public void setFrameProvider(RpfFrameProvider fp) {
		frameProvider = fp;
		if (this.cache != null) {
			this.cache.setFrameProvider(frameProvider);
		}
	}

	/**
	 * Return RpfFrameProvider used by the layer.
	 */
	public RpfFrameProvider getFrameProvider() {
		return frameProvider;
	}

	/**
	 * Returns the Vector containing RpfCoverageBoxes that was returned from the
	 * RpfFrameProvider as a result of the last setCache call. These provide
	 * rudimentary knowledge about what is being displayed. This vector is from
	 * the primary cache handler.
	 * 
	 * @return Vector of RpfCoverageBoxes.
	 */
	public List<RpfCoverageBox> getCoverageBoxes() {
		return this.cache.getCoverageBoxes();
	}

	/**
	 * Prepares the graphics for the layer. This is where the getRectangle()
	 * method call is made on the rpf.
	 * <p>
	 * Occasionally it is necessary to abort a prepare call. When this happens,
	 * the map will set the cancel bit in the LayerThread, (the thread that is
	 * running the prepare). If this Layer needs to do any cleanups during the
	 * abort, it should do so, but return out of the prepare asap.
	 * 
	 * @return graphics list of images and attributes.
	 */
	public synchronized OMGraphicList prepare() {

		Projection projection = getProjection();
		OMGraphicList retList = new OMGraphicList();
		retList.setTraverseMode(OMList.FIRST_ADDED_ON_TOP);

		if (frameProvider == null) {
			// Assuming running locally - otherwise the
			// frameProvider should be set before we get here,
			// like in setProperties or in the constructor.
			setPaths(paths);
			if (frameProvider == null) {
				// Doh! no paths were set!
				logger.warning(getName()
						+ ": null frame provider - either no RPF paths were set, or no frame provider was assigned.  The RpfLayer has no way to get RPF data.");
				return retList;
			}
		}

		if (coverage != null && coverage.isInUse()) {
			coverage.prepare(projection, viewAttributes.chartSeries);
			retList.add(coverage);
		}

		// Check the current minScale and maxScale set on the layer, ignore if
		// projection scale is out of range.
		if (!isProjectionOK(projection)) {
			return retList;
		}

		if (this.cache == null) {
			rpfLogger.fine(getName() + ": Creating cache!");
			this.cache = new RpfCacheManager(frameProvider, viewAttributes, subframeCacheSize, auxSubframeCacheSize);
		}

		// Check to make sure the projection is CADRG
		if (!(projection instanceof EqualArc) && (viewAttributes.showMaps || viewAttributes.showInfo)) {
			// fireRequestInfoLine("RpfLayer runs faster with an Equal Arc
			// projection (CADRG/LLXY).");
		}

		rpfLogger.fine(getName() + " doing it");

		// Setting the OMGraphicsList for this layer. Remember, the
		// OMGraphicList is made up of OMGraphics, which are generated
		// (projected) when the graphics are added to the list. So,
		// after this call, the list is ready for painting.

		// call getRectangle();
		if (rpfLogger.isLoggable(Level.FINE)) {
			rpfLogger.fine(getName() + "calling getRectangle " + " with projection: " + projection + " ul = "
					+ projection.getUpperLeft() + " lr = " + projection.getLowerRight());
		}

		if (frameProvider.needViewAttributeUpdates()) {
			frameProvider.setViewAttributes(viewAttributes);
		}

		try {

			// OMGraphics are generated by the RpfCacheHandlers when fetched
			retList.addAll(this.cache.getRectangle(projection));

			if (logger.isLoggable(Level.FINE)) {
				logger.fine(getName() + ": finished with " + retList.size() + " graphics");
			}

		} catch (java.lang.NullPointerException npe) {
			logger.warning(getName() + ": Something really bad happened - \n " + npe);
			npe.printStackTrace();
			retList = new OMGraphicList();
			this.cache = null;
		}

		return retList;
	}

	public boolean isHighlightable(OMGraphic omg) {
		return viewAttributes.showInfo && omg instanceof OMRaster && omg.isSelected();
	}

	public String getToolTipTextFor(OMGraphic omg) {
		return (String) omg.getAttribute(OMGraphic.TOOLTIP);
	}

	/**
	 * Overridden to do nothing because we want nothing to happen.
	 */
	public void highlight(OMGraphic omg) {

	}

	/**
	 * Overridden to do nothing because we want nothing to happen.
	 */
	public void unhighlight(OMGraphic omg) {

	}

	// ----------------------------------------------------------------------
	// GUI
	// ----------------------------------------------------------------------
	private transient JPanel box = null;

	/**
	 * Provides the palette widgets to control the options of showing maps, or
	 * attribute text.
	 * 
	 * @return Component object representing the palette widgets.
	 */
	public java.awt.Component getGUI() {
		if (box == null) {
			JCheckBox showInfoCheck, lockSeriesCheck;

			box = new JPanel();
			GridBagLayout gridbag = new GridBagLayout();
			GridBagConstraints c = new GridBagConstraints();
			box.setLayout(gridbag);

			c.anchor = GridBagConstraints.NORTHWEST;
			c.gridx = GridBagConstraints.REMAINDER;

			showInfoCheck = new JCheckBox("Show Attributes", viewAttributes.showInfo);
			showInfoCheck.setActionCommand(showInfoCommand);
			showInfoCheck.addActionListener(this);

			String tmpCS = viewAttributes.chartSeries;
			if (tmpCS == null) {
				tmpCS = RpfViewAttributes.ANY;
			}

			boolean locked = !tmpCS.equalsIgnoreCase(RpfViewAttributes.ANY);
			String lockedTitle = locked ? (lockedButtonTitle + " - " + tmpCS) : unlockedButtonTitle;

			lockSeriesCheck = new JCheckBox(lockedTitle, locked);
			lockSeriesCheck.setActionCommand(lockSeriesCommand);
			lockSeriesCheck.addActionListener(this);

			// box1.add(showMapsCheck);
			gridbag.setConstraints(showInfoCheck, c);
			box.add(showInfoCheck);
			gridbag.setConstraints(lockSeriesCheck, c);
			box.add(lockSeriesCheck);

			if (coverage != null) {
				JCheckBox showCoverageCheck = new JCheckBox("Show Coverage", coverage.isInUse());
				showCoverageCheck.setActionCommand(showCoverageCommand);
				showCoverageCheck.addActionListener(this);
				gridbag.setConstraints(showCoverageCheck, c);
				box.add(showCoverageCheck);
			}

			JPanel layerStuff = getDefaultSettingsPanel(RpfLayer.class, viewAttributes.opaqueness / 255f);
			gridbag.setConstraints(layerStuff, c);
			box.add(layerStuff);
		}
		return box;
	}

	// ----------------------------------------------------------------------
	// ActionListener interface implementation
	// ----------------------------------------------------------------------

	/**
	 * The Action Listener method, that reacts to the palette widgets actions.
	 */
	public void actionPerformed(ActionEvent e) {
		super.actionPerformed(e);
		String cmd = e.getActionCommand();
		if (cmd == showMapsCommand) {
			JCheckBox mapCheck = (JCheckBox) e.getSource();
			viewAttributes.showMaps = mapCheck.isSelected();
			repaint();
		} else if (cmd == showInfoCommand) {
			JCheckBox infoCheck = (JCheckBox) e.getSource();
			boolean showInfo = infoCheck.isSelected();
			viewAttributes.showInfo = showInfo;
			showSubframes(showInfo);
			doPrepare();
		} else if (cmd == lockSeriesCommand) {
			JCheckBox lockCheck = (JCheckBox) e.getSource();
			boolean locked = lockCheck.isSelected();
			if (locked) {
				List<RpfCoverageBox> coverageBoxes = getCoverageBoxes();
				String seriesName;

				if (coverageBoxes == null || coverageBoxes.isEmpty()) {
					seriesName = RpfViewAttributes.ANY;
				} else {
					seriesName = coverageBoxes.get(0).chartCode;
				}

				if (seriesName == null) {
					seriesName = RpfViewAttributes.ANY;
					fireRequestMessage("The " + getName()
							+ " Layer is having trouble determining what kind\nof charts are being displayed.  Can't establish lock for charts\ncurrently being viewed.");
				}

				lockCheck.setText(lockedButtonTitle + " - " + seriesName);
				viewAttributes.chartSeries = seriesName;

			} else {
				lockCheck.setText(unlockedButtonTitle);
				viewAttributes.chartSeries = RpfViewAttributes.ANY;
			}

		} else if (cmd == showCoverageCommand) {
			if (coverage != null) {
				JCheckBox coverageCheck = (JCheckBox) e.getSource();
				coverage.setInUse(coverageCheck.isSelected());
				doPrepare();
			}
		} else {
			// logger.warning("RpfLayer: Unknown action command \"" + cmd
			// +
			// "\" in RpfLayer.actionPerformed().");

			// OK, not really sure what happened, just act like a
			// reset.
			doPrepare();
		}
	}

	protected void showSubframes(boolean show) {
		OMGraphicList list = getList();
		if (list != null) {
			list.setSelected(show);
		}

		MapMouseInterpreter mmi = getMouseEventInterpreter();
		if (mmi != null) {
			mmi.setActive(show);
		}
	}

	/** Print out the contents of a properties file. */
	public static void main(String[] argv) {
		System.out.println("#########################################");
		System.out.println("# Properties for the JAVA RpfLayer");
		System.out.println("# Mandatory properties:");
		System.out.println("layer.class=com.bbn.openmap.layer.rpf.RpfLayer");
		System.out.println("layer.prettyName=CADRG");
		System.out.println("# This property should reflect the paths to the RPF directories");
		System.out.println("layer.paths=<Path to RPF dir>;/cdrom/cdrom0/RPF");
		System.out.println(
				"# Optional properties - Defaults will be set for properties not included (defaults are listed):");
		System.out.println("# Number between 0-255: 0 is transparent, 255 is opaque");
		System.out.println("layer.opaque=255");
		System.out.println("# Number of colors to use on the maps - 16, 32, 216");
		System.out.println("layer.numberColors=216");
		System.out.println("# Display maps on startup");
		System.out.println("layer.showMaps=true");
		System.out.println("# Display attribute information on startup");
		System.out.println("layer.showInfo=false");
		System.out.println("# Scale images to match map scale");
		System.out.println("layer.scaleImages=true");
		System.out.println(
				"# The scale factor to allow when scaling images (2x, 4x, also mean 1/2, 1/4).  Default is 4.");
		System.out.println("rpf.imageScaleFactor=4");
		System.out.println("# Reset the cache if layer is removed from map");
		System.out.println("layer.killCache=false");
		System.out.println("# Limit the display to the chart code specified. (GN, JN, ON, TP, etc.)");
		System.out.println("layer.chartSeries=ANY");
		System.out.println("# Set the subframe cache size. (Number of subframes to hold on to, 256x256 pixels");
		System.out.println("layer.subframeCacheSize=128");
		System.out.println("# Get the subframe attribute data from the frame provider.");
		System.out.println("rpf.autofetchAttributes=false");
		System.out.println("#If you want the coverage tool to be available");
		System.out.println("layer.coverage=true");
		System.out.println("#Then add coverage constants as needed.");
	}
}